﻿<#
	File      = Use-PspxModule.ps1
	Author    = Kirk "Poshoholic" Munro
	Publisher = Poshoholic Studios
	Copyright = © 2012 Poshoholic Studios. All rights reserved.
#>

function Use-PspxModule {
	[CmdletBinding()]
	param(
		[Parameter(Position=0, Mandatory=$true)]
		[ValidateNotNull()]
		[System.Management.Automation.ScriptBlock]
		$ScriptBlock
	)

	try {
		#region Hide all variables in the this scope to maintain invocation transparency.

		<#
		PowerShell 3.0 Breaking Change:
		In PowerShell 2.0, you could change any non-read-only and non-constant variable to private.
		This has changed in PowerShell 3.0 for a small set of variables. There is no known workaround
		that completely recovers from this. The best option is to use -ErrorAction SilentlyContinue
		when calling Set-Variable in this case.
		#>
		foreach ($variable in @(Get-Variable -Scope 0 -ErrorAction SilentlyContinue | Where-Object {($_.Options -band -bnot ([System.Management.Automation.ScopedItemOptions]::ReadOnly -bor [System.Management.Automation.ScopedItemOptions]::Constant)) -eq $_.Options})) {
			Set-Variable -Name $variable.Name -Scope 0 -Option Private -ErrorAction SilentlyContinue
		}

		#endregion

		#region Extend the script block with reassignment of variables to maintain transparency.

		$private:ExtendedScriptBlock = {
			#region Reassign writable variables to maintain invocation transparency.

			foreach ($variable in @(Get-Variable -Scope 2 -ErrorAction SilentlyContinue | Where-Object {($_.Options -band -bnot ([System.Management.Automation.ScopedItemOptions]::ReadOnly -bor [System.Management.Automation.ScopedItemOptions]::Constant -bor [System.Management.Automation.ScopedItemOptions]::Private)) -eq $_.Options})) {
				Set-Variable -Name $variable.Name -Scope 0 -Value $variable.Value -Option $variable.Options
			}

			#endregion

			#region Dot-source the script block that was passed in.

			. (Get-Variable -Name ScriptBlock -Scope 1 -ValueOnly)

			#endregion
		}

		#endregion

		if (Get-Module -Name pspx) {
			#region If pspx is installed and loaded, simply invoke the script block.

			& $private:ExtendedScriptBlock

			#endregion
		} else {
			#region Otherwise try to load pspx, invoke the script block, and then unload pspx.

			Import-Module -Name pspx -ErrorAction SilentlyContinue
			if (-not $?) {
				Write-Warning -Message $StringTable.WarningPspxModuleNotFound
			} else {
				try {
					& $private:ExtendedScriptBlock
				} finally {
					Remove-Module -Name pspx -ErrorAction SilentlyContinue
				}
			}

			#endregion
		}
	} catch {
		throw
	}
}